/*******************************************************************************
* Copyright (c) 2012 
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* zhangliang - initial API and implementation
*******************************************************************************/

#ifndef SERVICE_H
#define SERVICE_H

#include "handle.h"
#include "singleton.h"

struct CoreMessage;

/*!
 * @brief
 *  application service
 * @note
 *  local service load one *.so object and hold a msg queue
 *  remote service has no *.so loaded and no msg queue, it's expected to 
 *  forward the msgs which was sended to it to harbor service, waiting to be
 *  shipped across the net to services on other shores
 */
class Service {
 public:
  typedef boost::function<void (void* service, 
                                uint32_t handle,
                                uint32_t session, 
                                const char* data, 
                                uint16_t len)> MessageHandler;
 public:
  Service(Handle h, const std::string& name, 
          const std::string& module_name, const std::string& args);

  /*!
   * @brief
   *  init module
   * @note
   *  called after the service is correctly registered
   */
  int32_t Init(void); 

  /*!
   * @brief
   *  register message handler
   */
  void SetMsgHandler(const MessageHandler& mh);

  /*!
   * @brief
   *  push a message to this service
   */
  void Push(const CoreMessage& msg);

  /*!
   * @brief
   *  allocate a new session id
   */
  uint32_t AllocSession(void);
  
  /*!
   * @brief
   *  get service name
   */
  const std::string& name(void) const;

  /*!
   * @brief
   *  get handle
   */
  const Handle& handle(void) const;

  /*!
   * @brief
   *  get context data of module runtime
   */
  void* context(void);

 protected:
  class ServiceImpl;
  boost::scoped_ptr<Service::ServiceImpl> pimpl_;
};

typedef boost::shared_ptr<Service> ServicePtr;

class Services : public Singleton<Services> {
 public:
  Services(void);

  void Init(void);

  /*!
   * @brief
   *  create a new service
   * @param name
   *  if null, then this is an unnamed service
   *  else this is the name of the service
   * @param module_name
   *  name of the <module_name>.so 
   * @param args
   *  args to be passed to module
   * @note
   *  service naming convention:
   *  1. local service > .name
   *  2. uname service > :1234ABCD
   *  3. named service > name
   *  note that local/uname service is invisible to other services
   *  until which is received a message from it, so only named service need
   *  to be registered on the network.
   *  if a local/uname service is known to others, saying that its handle
   *  is known to others, sending message to such service need no queries
   *  to name cache, handle itself has enough info to get things done.
   */
  Handle New(const char* name, const char* module_name, const char* args);

  /*!
   * @brief
   *  make the handle and the service it pointed to unavailable
   * @note
   *  with the use of shared_ptr, the service ptr may still be usable
   *  but can't be index from Services
   *  TODO: is this the behavior i want ?
   */
  void Retire(Handle handle);

  /*!
   * @brief
   *  query by name/handle
   */
  ServicePtr  service(Handle handle);
  ServicePtr  service(const char* name);
  
  /*!
   * @brief
   *  send message to service
   * @param destination
   *  the handle of designated service
   * @param message
   *  message to be send
   * @note
   *  if the msg pointed to local service, 
   *  just put the msg into the msg queue of the target service
   *  if the msg pointed to remote service,
   *  hand the msg to ejector service
   */
  void Send(const Handle& destination, const CoreMessage& message);

  /*!
   * @brief
   *  same as above except destination is designated by service name
   */
  void Send(const char* destination, const CoreMessage& message);

 private:
  Handle NextHandle(void);

 private:
  // map : handle -> service
  boost::unordered_map<
      Handle, 
      ServicePtr, 
      std::size_t (*)(const Handle&)> handle_map_;

  uint32_t handle_serial_number_;
  uint32_t svr_id_;
  boost::mutex mutex_;
  boost::weak_ptr<Service> ejector_ptr_;
};

#endif // SERVICE_H

